{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<a href=\"https://colab.research.google.com/github/meta-llama/llama-recipes/blob/main/recipes/use_cases/agents/langchain/langgraph-rag-agent-local.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Local LangChain Vector + Graph Ingestion with Llama 3\n",
    "\n",
    "\n",
    "Simple example of ingesting data with Llama 3 to both Milvus and Neo4j databases.\n",
    "\n",
    "\n",
    "\n",
    "## Local models\n",
    "\n",
    "### LLM\n",
    "\n",
    "Use [Ollama](https://ollama.ai/) and [llama3](https://ollama.ai/library/llama3):\n",
    "\n",
    "```\n",
    "ollama pull llama3.1\n",
    "```\n",
    "\n",
    "### Env Variables\n",
    "Variables needed in an .env file or loaded as variables at start:\n",
    "\n",
    "Required:\n",
    "```\n",
    "NEO4J_URI=...\n",
    "NEO4J_USERNAME=...\n",
    "NEO4J_PASSWORD=...\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pip install -U beautifulsoup4 langchain langchain_community langchain-experimental langchain-huggingface langchain-milvus neo4j sentence_transformers tiktoken pymilvus"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "### Load credentials from .env file\n",
    "from dotenv import load_dotenv\n",
    "\n",
    "load_dotenv()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.globals import set_verbose, set_debug\n",
    "\n",
    "set_debug(True)\n",
    "set_verbose(True)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "### Milvus Lite Vectorstore\n",
    "\n",
    "from langchain.text_splitter import RecursiveCharacterTextSplitter\n",
    "from langchain_community.document_loaders import WebBaseLoader\n",
    "from langchain_milvus import Milvus\n",
    "from langchain_community.embeddings import HuggingFaceEmbeddings\n",
    "\n",
    "urls = [\n",
    "    \"https://lilianweng.github.io/posts/2023-06-23-agent/\",\n",
    "    \"https://lilianweng.github.io/posts/2023-03-15-prompt-engineering/\",\n",
    "    \"https://lilianweng.github.io/posts/2023-10-25-adv-attack-llm/\",\n",
    "]\n",
    "\n",
    "docs = [WebBaseLoader(url).load() for url in urls]\n",
    "docs_list = [item for sublist in docs for item in sublist if item]\n",
    "text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(\n",
    "    chunk_size=250, chunk_overlap=0\n",
    ")\n",
    "doc_splits = text_splitter.split_documents(docs_list)\n",
    "\n",
    "print(f'Number of docs: {len(docs_list)}')\n",
    "print(f'Number of chunks: {len(doc_splits)}')\n",
    "\n",
    "# Add to Milvus\n",
    "vectorstore = Milvus.from_documents(\n",
    "    documents=doc_splits,\n",
    "    collection_name=\"rag_milvus\",\n",
    "    embedding=HuggingFaceEmbeddings(),\n",
    "    connection_args={\"uri\": \"./milvus_ingest.db\"},\n",
    "\n",
    ")\n",
    "retriever = vectorstore.as_retriever()\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Neo4j Graphstore\n",
    "from langchain_community.graphs import Neo4jGraph\n",
    "from langchain_experimental.graph_transformers import LLMGraphTransformer\n",
    "from langchain_experimental.llms.ollama_functions import OllamaFunctions\n",
    "\n",
    "# Initialize Neo4j\n",
    "graph = Neo4jGraph()\n",
    "\n",
    "# Graph Conversion requires function calling enabled llm\n",
    "graph_llm = OllamaFunctions(model=\"llama3.1\", format=\"json\")\n",
    "\n",
    "# Filtered graph transformer\n",
    "graph_transformer = LLMGraphTransformer(\n",
    "    llm=graph_llm,\n",
    "    allowed_nodes=[\"Person\",\"Concept\",\"Technology\"],\n",
    "    node_properties=[\"name\",\"description\",\"source\"],\n",
    "    allowed_relationships=[\"WROTE\", \"MENTIONS\", \"RELATED_TO\"],\n",
    "    strict_mode=False,\n",
    ")\n",
    "\n",
    "# Convert list of Document objects to Graph Document\n",
    "graph_documents = graph_transformer.convert_to_graph_documents(doc_splits)\n",
    "\n",
    "# Filter Graph Documents with no nodes and relationships\n",
    "filtered_graph_documents = [g_doc for g_doc in graph_documents if len(g_doc.nodes) > 0 or len(g_doc.relationships) > 0]\n",
    "\n",
    "# Add Graph Documents to Neo4j\n",
    "graph.add_graph_documents(filtered_graph_documents)\n",
    "\n",
    "print(f\"Graph documents pre-filter: {len(graph_documents)}, post-filter: {len(filtered_graph_documents)}\")\n",
    "print(f'1st Graph Doc: {filtered_graph_documents[0].__dict__}')\n",
    "print(f\"Nodes from 1st graph doc:{filtered_graph_documents[0].nodes}\")\n",
    "print(f\"Relationships from 1st graph doc:{filtered_graph_documents[0].relationships}\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
